// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/sender/audio_encoder.h"

#include <stdint.h>

#include <algorithm>
#include <limits>
#include <string>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/sys_byteorder.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "media/base/audio_sample_types.h"
#include "media/cast/common/rtp_time.h"
#include "media/cast/constants.h"

#if !defined(OS_IOS)
#include "third_party/opus/src/include/opus.h"
#endif

#if defined(OS_MACOSX)
#include <AudioToolbox/AudioToolbox.h>
#endif

namespace media {
namespace cast {

    namespace {

        const int kUnderrunSkipThreshold = 3;
        const int kDefaultFramesPerSecond = 100;

    } // namespace

    // Base class that handles the common problem of feeding one or more AudioBus'
    // data into a buffer and then, once the buffer is full, encoding the signal and
    // emitting a SenderEncodedFrame via the FrameEncodedCallback.
    //
    // Subclasses complete the implementation by handling the actual encoding
    // details.
    class AudioEncoder::ImplBase
        : public base::RefCountedThreadSafe<AudioEncoder::ImplBase> {
    public:
        ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
            Codec codec,
            int num_channels,
            int sampling_rate,
            int samples_per_frame,
            const FrameEncodedCallback& callback)
            : cast_environment_(cast_environment)
            , codec_(codec)
            , num_channels_(num_channels)
            , samples_per_frame_(samples_per_frame)
            , callback_(callback)
            , operational_status_(STATUS_UNINITIALIZED)
            , frame_duration_(base::TimeDelta::FromMicroseconds(
                  base::Time::kMicrosecondsPerSecond * samples_per_frame_ / sampling_rate))
            , buffer_fill_end_(0)
            , frame_id_(FrameId::first())
            , samples_dropped_from_buffer_(0)
        {
            // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
            const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100;
            if (num_channels_ <= 0 || samples_per_frame_ <= 0 || frame_duration_.is_zero() || samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) {
                operational_status_ = STATUS_INVALID_CONFIGURATION;
            }
        }

        OperationalStatus InitializationResult() const
        {
            return operational_status_;
        }

        int samples_per_frame() const
        {
            return samples_per_frame_;
        }

        base::TimeDelta frame_duration() const { return frame_duration_; }

        void EncodeAudio(std::unique_ptr<AudioBus> audio_bus,
            const base::TimeTicks& recorded_time)
        {
            DCHECK_EQ(operational_status_, STATUS_INITIALIZED);
            DCHECK(!recorded_time.is_null());

            // Determine whether |recorded_time| is consistent with the amount of audio
            // data having been processed in the past.  Resolve the underrun problem by
            // dropping data from the internal buffer and skipping ahead the next
            // frame's RTP timestamp by the estimated number of frames missed.  On the
            // other hand, don't attempt to resolve overruns: A receiver should
            // gracefully deal with an excess of audio data.
            base::TimeDelta buffer_fill_duration = buffer_fill_end_ * frame_duration_ / samples_per_frame_;
            if (!frame_capture_time_.is_null()) {
                const base::TimeDelta amount_ahead_by = recorded_time - (frame_capture_time_ + buffer_fill_duration);
                const int64_t num_frames_missed = amount_ahead_by / frame_duration_;
                if (num_frames_missed > kUnderrunSkipThreshold) {
                    samples_dropped_from_buffer_ += buffer_fill_end_;
                    buffer_fill_end_ = 0;
                    buffer_fill_duration = base::TimeDelta();
                    frame_rtp_timestamp_ += RtpTimeDelta::FromTicks(num_frames_missed * samples_per_frame_);
                    DVLOG(1) << "Skipping RTP timestamp ahead to account for "
                             << num_frames_missed * samples_per_frame_
                             << " samples' worth of underrun.";
                    TRACE_EVENT_INSTANT2("cast.stream", "Audio Skip",
                        TRACE_EVENT_SCOPE_THREAD,
                        "frames missed", num_frames_missed,
                        "samples dropped", samples_dropped_from_buffer_);
                }
            }
            frame_capture_time_ = recorded_time - buffer_fill_duration;

            // Encode all audio in |audio_bus| into zero or more frames.
            int src_pos = 0;
            while (src_pos < audio_bus->frames()) {
                // Note: This is used to compute the encoder utilization and so it uses
                // the real-world clock instead of the CastEnvironment clock, the latter
                // of which might be simulated.
                const base::TimeTicks start_time = base::TimeTicks::Now();

                const int num_samples_to_xfer = std::min(
                    samples_per_frame_ - buffer_fill_end_, audio_bus->frames() - src_pos);
                DCHECK_EQ(audio_bus->channels(), num_channels_);
                TransferSamplesIntoBuffer(
                    audio_bus.get(), src_pos, buffer_fill_end_, num_samples_to_xfer);
                src_pos += num_samples_to_xfer;
                buffer_fill_end_ += num_samples_to_xfer;

                if (buffer_fill_end_ < samples_per_frame_)
                    break;

                std::unique_ptr<SenderEncodedFrame> audio_frame(new SenderEncodedFrame());
                audio_frame->dependency = EncodedFrame::KEY;
                audio_frame->frame_id = frame_id_;
                audio_frame->referenced_frame_id = frame_id_;
                audio_frame->rtp_timestamp = frame_rtp_timestamp_;
                audio_frame->reference_time = frame_capture_time_;

                TRACE_EVENT_ASYNC_BEGIN2("cast.stream", "Audio Encode", audio_frame.get(),
                    "frame_id", frame_id_.lower_32_bits(),
                    "rtp_timestamp",
                    frame_rtp_timestamp_.lower_32_bits());
                if (EncodeFromFilledBuffer(&audio_frame->data)) {
                    // Compute encoder utilization as the real-world time elapsed divided
                    // by the signal duration.
                    audio_frame->encoder_utilization = (base::TimeTicks::Now() - start_time).InSecondsF() / frame_duration_.InSecondsF();

                    TRACE_EVENT_ASYNC_END1("cast.stream", "Audio Encode", audio_frame.get(),
                        "encoder_utilization",
                        audio_frame->encoder_utilization);

                    audio_frame->encode_completion_time = cast_environment_->Clock()->NowTicks();
                    cast_environment_->PostTask(
                        CastEnvironment::MAIN,
                        FROM_HERE,
                        base::Bind(callback_,
                            base::Passed(&audio_frame),
                            samples_dropped_from_buffer_));
                    samples_dropped_from_buffer_ = 0;
                }

                // Reset the internal buffer, frame ID, and timestamps for the next frame.
                buffer_fill_end_ = 0;
                ++frame_id_;
                frame_rtp_timestamp_ += RtpTimeDelta::FromTicks(samples_per_frame_);
                frame_capture_time_ += frame_duration_;
            }
        }

    protected:
        friend class base::RefCountedThreadSafe<ImplBase>;
        virtual ~ImplBase() { }

        virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
            int source_offset,
            int buffer_fill_offset,
            int num_samples)
            = 0;
        virtual bool EncodeFromFilledBuffer(std::string* out) = 0;

        const scoped_refptr<CastEnvironment> cast_environment_;
        const Codec codec_;
        const int num_channels_;
        const int samples_per_frame_;
        const FrameEncodedCallback callback_;

        // Subclass' ctor is expected to set this to STATUS_INITIALIZED.
        OperationalStatus operational_status_;

        // The duration of one frame of encoded audio samples. Derived from
        // |samples_per_frame_| and the sampling rate.
        const base::TimeDelta frame_duration_;

    private:
        // In the case where a call to EncodeAudio() cannot completely fill the
        // buffer, this points to the position at which to populate data in a later
        // call.
        int buffer_fill_end_;

        // A counter used to label EncodedFrames.
        FrameId frame_id_;

        // The RTP timestamp for the next frame of encoded audio.  This is defined as
        // the number of audio samples encoded so far, plus the estimated number of
        // samples that were missed due to data underruns.  A receiver uses this value
        // to detect gaps in the audio signal data being provided.
        RtpTimeTicks frame_rtp_timestamp_;

        // The local system time associated with the start of the next frame of
        // encoded audio.  This value is passed on to a receiver as a reference clock
        // timestamp for the purposes of synchronizing audio and video.  Its
        // progression is expected to drift relative to the elapsed time implied by
        // the RTP timestamps.
        base::TimeTicks frame_capture_time_;

        // Set to non-zero to indicate the next output frame skipped over audio
        // samples in order to recover from an input underrun.
        int samples_dropped_from_buffer_;

        DISALLOW_COPY_AND_ASSIGN(ImplBase);
    };

#if !defined(OS_IOS)
    class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
    public:
        OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
            int num_channels,
            int sampling_rate,
            int bitrate,
            const FrameEncodedCallback& callback)
            : ImplBase(cast_environment,
                CODEC_AUDIO_OPUS,
                num_channels,
                sampling_rate,
                sampling_rate / kDefaultFramesPerSecond, /* 10 ms frames */
                callback)
            , encoder_memory_(new uint8_t[opus_encoder_get_size(num_channels)])
            , opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get()))
            , buffer_(new float[num_channels * samples_per_frame_])
        {
            if (ImplBase::operational_status_ != STATUS_UNINITIALIZED || sampling_rate % samples_per_frame_ != 0 || !IsValidFrameDuration(frame_duration_)) {
                return;
            }
            if (opus_encoder_init(opus_encoder_,
                    sampling_rate,
                    num_channels,
                    OPUS_APPLICATION_AUDIO)
                != OPUS_OK) {
                ImplBase::operational_status_ = STATUS_INVALID_CONFIGURATION;
                return;
            }
            ImplBase::operational_status_ = STATUS_INITIALIZED;

            if (bitrate <= 0) {
                // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a
                // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms
                // frame size.  The opus library authors may, of course, adjust this in
                // later versions.
                bitrate = OPUS_AUTO;
            }
            CHECK_EQ(opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)),
                OPUS_OK);
        }

    private:
        ~OpusImpl() final { }

        void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
            int source_offset,
            int buffer_fill_offset,
            int num_samples) final
        {
            DCHECK_EQ(audio_bus->channels(), num_channels_);
            float* dest = buffer_.get() + (buffer_fill_offset * num_channels_);
            audio_bus->ToInterleavedPartial<Float32SampleTypeTraits>(source_offset,
                num_samples, dest);
        }

        bool EncodeFromFilledBuffer(std::string* out) final
        {
            out->resize(kOpusMaxPayloadSize);
            const opus_int32 result = opus_encode_float(
                opus_encoder_, buffer_.get(), samples_per_frame_,
                reinterpret_cast<uint8_t*>(base::string_as_array(out)),
                kOpusMaxPayloadSize);
            if (result > 1) {
                out->resize(result);
                return true;
            } else if (result < 0) {
                LOG(ERROR) << "Error code from opus_encode_float(): " << result;
                return false;
            } else {
                // Do nothing: The documentation says that a return value of zero or
                // one byte means the packet does not need to be transmitted.
                return false;
            }
        }

        static bool IsValidFrameDuration(base::TimeDelta duration)
        {
            // See https://tools.ietf.org/html/rfc6716#section-2.1.4
            return duration == base::TimeDelta::FromMicroseconds(2500) || duration == base::TimeDelta::FromMilliseconds(5) || duration == base::TimeDelta::FromMilliseconds(10) || duration == base::TimeDelta::FromMilliseconds(20) || duration == base::TimeDelta::FromMilliseconds(40) || duration == base::TimeDelta::FromMilliseconds(60);
        }

        const std::unique_ptr<uint8_t[]> encoder_memory_;
        OpusEncoder* const opus_encoder_;
        const std::unique_ptr<float[]> buffer_;

        // This is the recommended value, according to documentation in
        // third_party/opus/src/include/opus.h, so that the Opus encoder does not
        // degrade the audio due to memory constraints.
        //
        // Note: Whereas other RTP implementations do not, the cast library is
        // perfectly capable of transporting larger than MTU-sized audio frames.
        static const int kOpusMaxPayloadSize = 4000;

        DISALLOW_COPY_AND_ASSIGN(OpusImpl);
    };
#endif

#if defined(OS_MACOSX)
    class AudioEncoder::AppleAacImpl : public AudioEncoder::ImplBase {
        // AAC-LC has two access unit sizes (960 and 1024). The Apple encoder only
        // supports the latter.
        static const int kAccessUnitSamples = 1024;

        // Size of an ADTS header (w/o checksum). See
        // http://wiki.multimedia.cx/index.php?title=ADTS
        static const int kAdtsHeaderSize = 7;

    public:
        AppleAacImpl(const scoped_refptr<CastEnvironment>& cast_environment,
            int num_channels,
            int sampling_rate,
            int bitrate,
            const FrameEncodedCallback& callback)
            : ImplBase(cast_environment,
                CODEC_AUDIO_AAC,
                num_channels,
                sampling_rate,
                kAccessUnitSamples,
                callback)
            , input_buffer_(AudioBus::Create(num_channels, kAccessUnitSamples))
            , input_bus_(AudioBus::CreateWrapper(num_channels))
            , max_access_unit_size_(0)
            , output_buffer_(nullptr)
            , converter_(nullptr)
            , file_(nullptr)
            , num_access_units_(0)
        {
            if (ImplBase::operational_status_ != STATUS_UNINITIALIZED) {
                return;
            }
            if (!Initialize(sampling_rate, bitrate)) {
                ImplBase::operational_status_ = STATUS_INVALID_CONFIGURATION;
                return;
            }
            ImplBase::operational_status_ = STATUS_INITIALIZED;
        }

    private:
        ~AppleAacImpl() final { Teardown(); }

        // Destroys the existing audio converter and file, if any.
        void Teardown()
        {
            if (converter_) {
                AudioConverterDispose(converter_);
                converter_ = nullptr;
            }
            if (file_) {
                AudioFileClose(file_);
                file_ = nullptr;
            }
        }

        // Initializes the audio converter and file. Calls Teardown to destroy any
        // existing state. This is so that Initialize() may be called to setup another
        // converter after a non-resumable interruption.
        bool Initialize(int sampling_rate, int bitrate)
        {
            // Teardown previous audio converter and file.
            Teardown();

            // Input data comes from AudioBus objects, which carry non-interleaved
            // packed native-endian float samples. Note that in Core Audio, a frame is
            // one sample across all channels at a given point in time. When describing
            // a non-interleaved samples format, the "per frame" fields mean "per
            // channel" or "per stream", with the exception of |mChannelsPerFrame|. For
            // uncompressed formats, one packet contains one frame.
            AudioStreamBasicDescription in_asbd;
            in_asbd.mSampleRate = sampling_rate;
            in_asbd.mFormatID = kAudioFormatLinearPCM;
            in_asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
            in_asbd.mChannelsPerFrame = num_channels_;
            in_asbd.mBitsPerChannel = sizeof(float) * 8;
            in_asbd.mFramesPerPacket = 1;
            in_asbd.mBytesPerPacket = in_asbd.mBytesPerFrame = sizeof(float);
            in_asbd.mReserved = 0;

            // Request AAC-LC encoding, with no downmixing or downsampling.
            AudioStreamBasicDescription out_asbd;
            memset(&out_asbd, 0, sizeof(AudioStreamBasicDescription));
            out_asbd.mSampleRate = sampling_rate;
            out_asbd.mFormatID = kAudioFormatMPEG4AAC;
            out_asbd.mChannelsPerFrame = num_channels_;
            UInt32 prop_size = sizeof(out_asbd);
            if (AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
                    0,
                    nullptr,
                    &prop_size,
                    &out_asbd)
                != noErr) {
                return false;
            }

            if (AudioConverterNew(&in_asbd, &out_asbd, &converter_) != noErr) {
                return false;
            }

            // The converter will fully specify the output format and update the
            // relevant fields of the structure, which we can now query.
            prop_size = sizeof(out_asbd);
            if (AudioConverterGetProperty(converter_,
                    kAudioConverterCurrentOutputStreamDescription,
                    &prop_size,
                    &out_asbd)
                != noErr) {
                return false;
            }

            // If bitrate is <= 0, allow the encoder to pick a suitable value.
            // Otherwise, set the bitrate (which can fail if the value is not suitable
            // or compatible with the output sampling rate or channels).
            if (bitrate > 0) {
                prop_size = sizeof(int);
                if (AudioConverterSetProperty(
                        converter_, kAudioConverterEncodeBitRate, prop_size, &bitrate)
                    != noErr) {
                    return false;
                }
            }

            // Figure out the maximum size of an access unit that the encoder can
            // produce. |mBytesPerPacket| will be 0 for variable size configurations,
            // in which case we must query the value.
            uint32_t max_access_unit_size = out_asbd.mBytesPerPacket;
            if (max_access_unit_size == 0) {
                prop_size = sizeof(max_access_unit_size);
                if (AudioConverterGetProperty(
                        converter_,
                        kAudioConverterPropertyMaximumOutputPacketSize,
                        &prop_size,
                        &max_access_unit_size)
                    != noErr) {
                    return false;
                }
            }

            // This is the only location where the implementation modifies
            // |max_access_unit_size_|.
            const_cast<uint32_t&>(max_access_unit_size_) = max_access_unit_size;

            // Allocate a buffer to store one access unit. This is the only location
            // where the implementation modifies |access_unit_buffer_|.
            const_cast<std::unique_ptr<uint8_t[]>&>(access_unit_buffer_)
                .reset(new uint8_t[max_access_unit_size]);

            // Initialize the converter ABL. Note that the buffer size has to be set
            // before every encode operation, since the field is modified to indicate
            // the size of the output data (on input it indicates the buffer capacity).
            converter_abl_.mNumberBuffers = 1;
            converter_abl_.mBuffers[0].mNumberChannels = num_channels_;
            converter_abl_.mBuffers[0].mData = access_unit_buffer_.get();

            // The "magic cookie" is an encoder state vector required for decoding and
            // packetization. It is queried now from |converter_| then set on |file_|
            // after initialization.
            UInt32 cookie_size;
            if (AudioConverterGetPropertyInfo(converter_,
                    kAudioConverterCompressionMagicCookie,
                    &cookie_size,
                    nullptr)
                != noErr) {
                return false;
            }
            std::unique_ptr<uint8_t[]> cookie_data(new uint8_t[cookie_size]);
            if (AudioConverterGetProperty(converter_,
                    kAudioConverterCompressionMagicCookie,
                    &cookie_size,
                    cookie_data.get())
                != noErr) {
                return false;
            }

            if (AudioFileInitializeWithCallbacks(this,
                    &FileReadCallback,
                    &FileWriteCallback,
                    &FileGetSizeCallback,
                    &FileSetSizeCallback,
                    kAudioFileAAC_ADTSType,
                    &out_asbd,
                    0,
                    &file_)
                != noErr) {
                return false;
            }

            if (AudioFileSetProperty(file_,
                    kAudioFilePropertyMagicCookieData,
                    cookie_size,
                    cookie_data.get())
                != noErr) {
                return false;
            }

            // Initially the input bus points to the input buffer. See the comment on
            // |input_bus_| for more on this optimization.
            input_bus_->set_frames(kAccessUnitSamples);
            for (int ch = 0; ch < input_buffer_->channels(); ++ch) {
                input_bus_->SetChannelData(ch, input_buffer_->channel(ch));
            }

            return true;
        }

        void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
            int source_offset,
            int buffer_fill_offset,
            int num_samples) final
        {
            DCHECK_EQ(audio_bus->channels(), input_buffer_->channels());

            // See the comment on |input_bus_| for more on this optimization. Note that
            // we cannot elide the copy if the source offset would result in an
            // unaligned pointer.
            if (num_samples == kAccessUnitSamples && source_offset * sizeof(float) % AudioBus::kChannelAlignment == 0) {
                DCHECK_EQ(buffer_fill_offset, 0);
                for (int ch = 0; ch < audio_bus->channels(); ++ch) {
                    auto* samples = const_cast<float*>(audio_bus->channel(ch));
                    input_bus_->SetChannelData(ch, samples + source_offset);
                }
                return;
            }

            // Copy the samples into the input buffer.
            DCHECK_EQ(input_bus_->channel(0), input_buffer_->channel(0));
            audio_bus->CopyPartialFramesTo(
                source_offset, num_samples, buffer_fill_offset, input_buffer_.get());
        }

        bool EncodeFromFilledBuffer(std::string* out) final
        {
            // Reset the buffer size field to the buffer capacity.
            converter_abl_.mBuffers[0].mDataByteSize = max_access_unit_size_;

            // Encode the current input buffer. This is a sychronous call.
            OSStatus oserr;
            UInt32 io_num_packets = 1;
            AudioStreamPacketDescription packet_description;
            oserr = AudioConverterFillComplexBuffer(converter_,
                &ConverterFillDataCallback,
                this,
                &io_num_packets,
                &converter_abl_,
                &packet_description);
            if (oserr != noErr || io_num_packets == 0) {
                return false;
            }

            // Reserve space in the output buffer to write the packet.
            out->reserve(packet_description.mDataByteSize + kAdtsHeaderSize);

            // Set the current output buffer and emit an ADTS-wrapped AAC access unit.
            // This is a synchronous call. After it returns, reset the output buffer.
            output_buffer_ = out;
            oserr = AudioFileWritePackets(file_,
                false,
                converter_abl_.mBuffers[0].mDataByteSize,
                &packet_description,
                num_access_units_,
                &io_num_packets,
                converter_abl_.mBuffers[0].mData);
            output_buffer_ = nullptr;
            if (oserr != noErr || io_num_packets == 0) {
                return false;
            }
            num_access_units_ += io_num_packets;
            return true;
        }

        // The |AudioConverterFillComplexBuffer| input callback function. Configures
        // the provided |AudioBufferList| to alias |input_bus_|. The implementation
        // can only supply |kAccessUnitSamples| samples as a result of not copying
        // samples or tracking read and write positions. Note that this function is
        // called synchronously by |AudioConverterFillComplexBuffer|.
        static OSStatus ConverterFillDataCallback(
            AudioConverterRef in_converter,
            UInt32* io_num_packets,
            AudioBufferList* io_data,
            AudioStreamPacketDescription** out_packet_desc,
            void* in_encoder)
        {
            DCHECK(in_encoder);
            auto* encoder = reinterpret_cast<AppleAacImpl*>(in_encoder);
            auto* input_buffer = encoder->input_buffer_.get();
            auto* input_bus = encoder->input_bus_.get();

            DCHECK_EQ(static_cast<int>(*io_num_packets), kAccessUnitSamples);
            DCHECK_EQ(io_data->mNumberBuffers,
                static_cast<unsigned>(input_bus->channels()));
            for (int i_buf = 0, end = io_data->mNumberBuffers; i_buf < end; ++i_buf) {
                io_data->mBuffers[i_buf].mNumberChannels = 1;
                io_data->mBuffers[i_buf].mDataByteSize = sizeof(float) * *io_num_packets;
                io_data->mBuffers[i_buf].mData = input_bus->channel(i_buf);

                // Reset the input bus back to the input buffer. See the comment on
                // |input_bus_| for more on this optimization.
                input_bus->SetChannelData(i_buf, input_buffer->channel(i_buf));
            }
            return noErr;
        }

        // The AudioFile read callback function.
        static OSStatus FileReadCallback(void* in_encoder,
            SInt64 in_position,
            UInt32 in_size,
            void* in_buffer,
            UInt32* out_size)
        {
            // This class only does writing.
            NOTREACHED();
            return kAudioFileNotOpenError;
        }

        // The AudioFile write callback function. Appends the data to the encoder's
        // current |output_buffer_|.
        static OSStatus FileWriteCallback(void* in_encoder,
            SInt64 in_position,
            UInt32 in_size,
            const void* in_buffer,
            UInt32* out_size)
        {
            DCHECK(in_encoder);
            DCHECK(in_buffer);
            auto* encoder = reinterpret_cast<const AppleAacImpl*>(in_encoder);
            auto* buffer = reinterpret_cast<const std::string::value_type*>(in_buffer);

            std::string* const output_buffer = encoder->output_buffer_;
            DCHECK(output_buffer);

            output_buffer->append(buffer, in_size);
            *out_size = in_size;
            return noErr;
        }

        // The AudioFile getsize callback function.
        static SInt64 FileGetSizeCallback(void* in_encoder)
        {
            // This class only does writing.
            NOTREACHED();
            return 0;
        }

        // The AudioFile setsize callback function.
        static OSStatus FileSetSizeCallback(void* in_encoder, SInt64 in_size)
        {
            return noErr;
        }

        // Buffer that holds one AAC access unit worth of samples. The input callback
        // function provides samples from this buffer via |input_bus_| to the encoder.
        const std::unique_ptr<AudioBus> input_buffer_;

        // Wrapper AudioBus used by the input callback function. Normally it wraps
        // |input_buffer_|. However, as an optimization when the client submits a
        // buffer containing exactly one access unit worth of samples, the bus is
        // redirected to the client buffer temporarily. We know that the base
        // implementation will call us right after to encode the buffer and thus we
        // can eliminate the copy into |input_buffer_|.
        const std::unique_ptr<AudioBus> input_bus_;

        // A buffer that holds one AAC access unit. Initialized in |Initialize| once
        // the maximum access unit size is known.
        const std::unique_ptr<uint8_t[]> access_unit_buffer_;

        // The maximum size of an access unit that the encoder can emit.
        const uint32_t max_access_unit_size_;

        // A temporary pointer to the current output buffer. Only non-null when
        // writing an access unit. Accessed by the AudioFile write callback function.
        std::string* output_buffer_;

        // The |AudioConverter| is responsible for AAC encoding. This is a Core Audio
        // object, not to be confused with |media::AudioConverter|.
        AudioConverterRef converter_;

        // The |AudioFile| is responsible for ADTS packetization.
        AudioFileID file_;

        // An |AudioBufferList| passed to the converter to store encoded samples.
        AudioBufferList converter_abl_;

        // The number of access units emitted so far by the encoder.
        uint64_t num_access_units_;

        DISALLOW_COPY_AND_ASSIGN(AppleAacImpl);
    };
#endif // defined(OS_MACOSX)

    class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
    public:
        Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
            int num_channels,
            int sampling_rate,
            const FrameEncodedCallback& callback)
            : ImplBase(cast_environment,
                CODEC_AUDIO_PCM16,
                num_channels,
                sampling_rate,
                sampling_rate / kDefaultFramesPerSecond, /* 10 ms frames */
                callback)
            , buffer_(new int16_t[num_channels * samples_per_frame_])
        {
            if (ImplBase::operational_status_ != STATUS_UNINITIALIZED)
                return;
            operational_status_ = STATUS_INITIALIZED;
        }

    private:
        ~Pcm16Impl() final { }

        void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
            int source_offset,
            int buffer_fill_offset,
            int num_samples) final
        {
            audio_bus->ToInterleavedPartial(
                source_offset, num_samples, sizeof(int16_t),
                buffer_.get() + buffer_fill_offset * num_channels_);
        }

        bool EncodeFromFilledBuffer(std::string* out) final
        {
            // Output 16-bit PCM integers in big-endian byte order.
            out->resize(num_channels_ * samples_per_frame_ * sizeof(int16_t));
            const int16_t* src = buffer_.get();
            const int16_t* const src_end = src + num_channels_ * samples_per_frame_;
            uint16_t* dest = reinterpret_cast<uint16_t*>(&out->at(0));
            for (; src < src_end; ++src, ++dest)
                *dest = base::HostToNet16(*src);
            return true;
        }

    private:
        const std::unique_ptr<int16_t[]> buffer_;

        DISALLOW_COPY_AND_ASSIGN(Pcm16Impl);
    };

    AudioEncoder::AudioEncoder(
        const scoped_refptr<CastEnvironment>& cast_environment,
        int num_channels,
        int sampling_rate,
        int bitrate,
        Codec codec,
        const FrameEncodedCallback& frame_encoded_callback)
        : cast_environment_(cast_environment)
    {
        // Note: It doesn't matter which thread constructs AudioEncoder, just so long
        // as all calls to InsertAudio() are by the same thread.
        insert_thread_checker_.DetachFromThread();
        switch (codec) {
#if !defined(OS_IOS)
        case CODEC_AUDIO_OPUS:
            impl_ = new OpusImpl(cast_environment,
                num_channels,
                sampling_rate,
                bitrate,
                frame_encoded_callback);
            break;
#endif
#if defined(OS_MACOSX)
        case CODEC_AUDIO_AAC:
            impl_ = new AppleAacImpl(cast_environment,
                num_channels,
                sampling_rate,
                bitrate,
                frame_encoded_callback);
            break;
#endif // defined(OS_MACOSX)
        case CODEC_AUDIO_PCM16:
            impl_ = new Pcm16Impl(cast_environment,
                num_channels,
                sampling_rate,
                frame_encoded_callback);
            break;
        default:
            NOTREACHED() << "Unsupported or unspecified codec for audio encoder";
            break;
        }
    }

    AudioEncoder::~AudioEncoder() { }

    OperationalStatus AudioEncoder::InitializationResult() const
    {
        DCHECK(insert_thread_checker_.CalledOnValidThread());
        if (impl_.get()) {
            return impl_->InitializationResult();
        }
        return STATUS_UNSUPPORTED_CODEC;
    }

    int AudioEncoder::GetSamplesPerFrame() const
    {
        DCHECK(insert_thread_checker_.CalledOnValidThread());
        if (InitializationResult() != STATUS_INITIALIZED) {
            NOTREACHED();
            return std::numeric_limits<int>::max();
        }
        return impl_->samples_per_frame();
    }

    base::TimeDelta AudioEncoder::GetFrameDuration() const
    {
        DCHECK(insert_thread_checker_.CalledOnValidThread());
        if (InitializationResult() != STATUS_INITIALIZED) {
            NOTREACHED();
            return base::TimeDelta();
        }
        return impl_->frame_duration();
    }

    void AudioEncoder::InsertAudio(std::unique_ptr<AudioBus> audio_bus,
        const base::TimeTicks& recorded_time)
    {
        DCHECK(insert_thread_checker_.CalledOnValidThread());
        DCHECK(audio_bus.get());
        if (InitializationResult() != STATUS_INITIALIZED) {
            NOTREACHED();
            return;
        }
        cast_environment_->PostTask(CastEnvironment::AUDIO,
            FROM_HERE,
            base::Bind(&AudioEncoder::ImplBase::EncodeAudio,
                impl_,
                base::Passed(&audio_bus),
                recorded_time));
    }

} // namespace cast
} // namespace media
